#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
    Module Documentation
    here
"""

# Created by  : Zhang Chengdong
# Create Date : 2024/11/22 13:44
# Version = v0.1.0

__author__ = "Zhang Chengdong"
__copyright__ = "Copyright 2024. Large scale model"
__credits__ = ['Zhang Chengdong']

__liscence__ = "MIT"
__version__ = "1.0.1"
__maintainer__ = "Zhang Chengdong"
__status__ = "Production"

import logging

import numpy as np
import pandas as pd
from datetime import datetime
from typing import List, Union, Dict
from collections import defaultdict
from django.conf import settings

from .model_architecture import NonLinearModel as non_model


def calculate_mixed_chemicals(ratio_key, data):
    """
    单个混合材的化学分析 计算细节
    :param ratio_key:
    :param data:
    :return:
    """
    ratio = data["搭配比例"][ratio_key]
    chemicals = data["materialAnalysis"]
    # 初始化混合物的化学成分
    mixed_chemicals = defaultdict(int)
    total_ratio = sum(ratio.values())
    for material, weight in ratio.items():
        for chemical, value in chemicals[material].items():
            mixed_chemicals[chemical] += round((value * weight) / total_ratio, 3)
    return mixed_chemicals


def get_feed_back_ingredients(data: dict) -> list:
    """
    获取使用物料的列表
    :param data:
    """
    feed_back_ingredients = list(set(list(data["feedbackIngredient"].keys())))
    return feed_back_ingredients


def deal_mixed_chemicals(data: dict):
    """

    :param data:
    :return:
    """
    data['搭配比例'] = defaultdict()
    if data.get("gypRatio", None) is not None:
        data['搭配比例']["石膏"] = data["gypRatio"]
        del data['gypRatio']
    for item in data['admixtureRatio']:
        data['搭配比例'][item] = data['admixtureRatio'][item]

    del data['admixtureRatio']

    for ratio_key in data["搭配比例"]:
        mixed_chemicals = calculate_mixed_chemicals(ratio_key, data)
        data["materialAnalysis"][ratio_key] = mixed_chemicals

    metric_list = get_feed_back_ingredients(data)

    for item in metric_list:
        metric_name = item
        if item not in data['materialAnalysis']:
            logging.info("==================时间：{}， {} 缺失！！！！！！！！！！,跳出=========================".format(
                data['productionDate'].strftime('%Y-%m-%d'), item))
            continue
        for ele_item in data['materialAnalysis'][item]:
            new_metric = "{}_new_{}".format(metric_name, ele_item)
            data[new_metric] = data["materialAnalysis"][item][ele_item]
            settings.need_columns_name.append(new_metric)
    del data['materialAnalysis']
    return data


def deal_process_data(data: dict):
    """
    处理过程质量
    :param data:
    :return:
    """
    for item in data['过程质量平均值']:
        new_name = "{}-{}".format("过程质量平均值", item)
        data[new_name] = data["过程质量平均值"][item]
        settings.need_columns_name.append(new_name)
    del data['过程质量平均值']
    return data


def deal_dcs_feedback(data: dict):
    """
    处理dcs反馈均值
    :param data:
    :return:
    """
    for item in data['DCS反馈配比平均值']:
        new_name = "{}-{}".format("DCS反馈配比平均值", item)
        data[new_name] = data['DCS反馈配比平均值'][item]
        settings.need_columns_name.append(new_name)
    del data['DCS反馈配比平均值']
    return data


def training_deal_data_for_model(data: dict):
    """
    将请求参数处理成需要的数据格式
    :param data:
    :return:
    """
    settings.need_columns_name = []
    # 熟料均化配置
    data['熟料3天强度预测'] = data['clinkerStrengthPrediction3d']
    data['熟料28天强度预测'] = data['clinkerStrengthPrediction28d']
    settings.need_columns_name.extend(['熟料3天强度预测', '熟料28天强度预测'])
    del data['clinkerStrengthPrediction3d']
    del data['clinkerStrengthPrediction28d']

    # 混合材和搭配比例计算化学分析
    data = deal_mixed_chemicals(data)

    # 处理过程质量
    data['过程质量平均值'] = data['processQuality']
    data['DCS反馈配比平均值'] = data['feedbackIngredient']
    del data['processQuality']
    del data['feedbackIngredient']
    data = deal_process_data(data)

    # 处理DCS反馈配比平均值
    data = deal_dcs_feedback(data)

    # 处理1,3天实测
    data['水泥1天实测值'] = data['checkStrength1d']
    data['水泥3天实测值'] = data['checkStrength3d']
    data['水泥28天实测值'] = data['checkStrength28d']
    settings.need_columns_name.extend(['水泥1天实测值', '水泥3天实测值', '水泥28天实测值'])

    del data['checkStrength1d']
    del data['checkStrength3d']
    del data['checkStrength28d']

    # 磨号和水泥品种和时间
    data['时间'] = data['productionDate']
    month = data['时间'].month
    data['月份'] = "{}月".format(month)
    data['时间'] = data['时间'].strftime("%Y-%m-%d")
    data['磨号'] = data['millCode']
    data['品种'] = data['productCode']
    del data['productCode']
    del data['millCode']
    del data['productionDate']
    settings.need_columns_name.extend(["时间", "月份", "磨号", "品种"])
    new_data_dict = defaultdict()
    for column in settings.need_columns_name:
        new_data_dict[column] = data[column]
    return new_data_dict


def compute_metrics_chemistry(x):
    """
    计算dcs反馈的统计中的物料化学分析
    """
    # 获取DCS反馈的数据 new
    dcs_columns_list = [col for col in x.keys().tolist() if "DCS反馈配比平均值-" in col]
    dcs_columns_list = ["DCS反馈配比平均值-熟料", "DCS反馈配比平均值-石灰石"]
    cao_count = 0.
    for item in dcs_columns_list:
        value = x[item]
        metric_name = item.replace("DCS反馈配比平均值-", "")
        cao_metric_chemistry = x["{}_new_CaO".format(metric_name)]
        cao_count += round(cao_metric_chemistry * value / 100., 2)
    return cao_count


def get_data_from_db_for_training(data: list) -> pd.DataFrame:
    """
    将mongodb数据库中获取的数据转化成pandas
    :param data:
    :return
    """
    data_list = []
    for item in data:
        item['feedbackIngredient'] = item['old_feedbackIngredient']
        del item['old_feedbackIngredient']
        if "checkStrength1d" not in item:
            item['checkStrength1d'] = np.NAN
        if item['deleted'] is True:
            continue
        item_data_dict = training_deal_data_for_model(item)
        data_list.append(item_data_dict)
    data_df = pd.DataFrame(data_list)
    data_df = data_df.loc[:, [col for col in data_df.columns if "水份" not in col]]
    # data_df['compute_cao'] = data_df.apply(compute_metrics_chemistry, axis=1)
    data_df = data_df.round(2)
    data_df['时间'] = pd.to_datetime(data_df['时间'])
    return data_df


def train_main(data: list, save_model_path: str, model_name: str = "CatBoost", mas_model_type: str = None,
               start_time: str = "2023-01-01", end_time: str = "2025-01-01"):
    """
    训练模型的主方法
    :param data
    :param save_model_path
    :param model_name
    :param mas_model_type
    :param start_time
    :param end_time
    :return:
    """
    # TODO 是否需要使用异步配合Redis进行训练
    start_time = datetime.strptime(start_time, "%Y-%m-%d")
    end_time = datetime.strptime(end_time, "%Y-%m-%d")
    data_df = get_data_from_db_for_training(data)
    # 时间过滤  TODO 上线前需要处理
    data_df = data_df.loc[(data_df['时间'] < end_time) & (data_df['时间'] >= start_time), :]
    data_df = data_df.loc[data_df['DCS反馈配比平均值-熟料'] >= 70, :]

    model = non_model(data_df, save_model_path, model_name, mas_model_type)

    # 训练3天模型，无一天
    model3_n1d_mse, model3_n1d_r2 = model.train_model_3d(actual1d=False)
    # model3_n1d_mse, model3_n1d_r2 = None, None
    logging.info("训练3天模型，无一天，训练的模型信息：MSE：{}，R2：{}".format(str(model3_n1d_mse), str(model3_n1d_r2)))
    # 训练3天模型,有一天
    model3_1d_mse, model3_1d_r2 = model.train_model_3d(actual1d=True)
    # model3_1d_mse, model3_1d_r2 = None, None
    logging.info("训练3天模型,有一天，训练的模型信息：MSE：{}，R2：{}".format(str(model3_1d_mse), str(model3_1d_r2)))
    #
    # # 训练28天模型，无一天
    model28_n1d_mse, model28_n1d_r2 = model.train_model_28d(actual1d=False, actual3d=False)
    # model28_n1d_mse, model28_n1d_r2 =  None, None
    logging.info("训练28天模型，无一天，训练的模型信息：MSE：{}，R2：{}".format(str(model28_n1d_mse), str(model28_n1d_r2)))
    #
    # # 训练28天模型，有一天，无3天
    model28_1d_mse, model28_1d_r2 = model.train_model_28d(actual1d=True, actual3d=False)
    # model28_1d_mse, model28_1d_r2 = None, None
    logging.info(
        "训练28天模型，有一天，无3天，训练的模型信息：MSE：{}，R2：{}".format(str(model28_1d_mse), str(model28_1d_r2)))
    #
    # # 训练28天模型，有一天，有3天
    model28_3d_mse, model28_3d_r2 = model.train_model_28d(actual1d=False, actual3d=True)
    # model28_3d_mse, model28_3d_r2 = None, None
    logging.info(
        "训练28天模型，有一天，有3天，训练的模型信息：MSE：{}，R2：{}".format(str(model28_3d_mse), str(model28_3d_r2)))

    return (model3_n1d_mse, model3_n1d_r2), (model3_1d_mse, model3_1d_r2), (model28_n1d_mse, model28_n1d_r2), (
        model28_1d_mse, model28_1d_r2), (model28_3d_mse, model28_3d_r2)
